package com.onpositive.knowledge.model.value.audits;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.semantic.model.api.meta.IMeta;
import com.onpositive.semantic.model.api.meta.IWritableMeta;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.knowledge.model.ICanBeActual;
import com.onpositive.knowledge.model.IThing;

public class ActualDocumentsAudit extends AuditSourceContainer implements IUniverseAudit {

	/**
	 * 
	 */
	private static final long serialVersionUID = 3657532238948414465L;
	public static final String ID = "actuial-documents-audit";
	private static final int actualPropertiesCount = 10 ;


	@Override
	public void perform( Iterable<IProperty> _properties, Iterable<Object> objects, AbstractAuditResult auditResult) {
		
		
		IProperty docNameProp =  getDocumentNameProperty(_properties);
		HashMap<IProperty,Integer> propStats = new HashMap<IProperty, Integer>() ;
		ArrayList<IProperty> properties = extractNonserviceProps( _properties ) ;
		initPropStats(properties, propStats);

//		for( Object obj : objects ){
//			String docName ;
//			try{
//				Object val = docNameProp.getValue(obj) ;
//				while( val instanceof IHasValue )
//					val = ((IHasValue)val).value() ;
//				
//				docName = (String) val ;
//				docStats.put( docName, 0) ;
//			}
//			catch( Exception e )
//			{
//				e.printStackTrace();
//			}
//		}
		
		if( !(auditResult instanceof OwnAuditResult) )
			return ;
		
		OwnAuditResult ownResult = (OwnAuditResult) auditResult ;

		ArrayList<Object> actualDocumentsArray = new ArrayList<Object>() ;
		gatherStats( properties, objects, propStats, docNameProp ) ;		
		processDocs( properties, objects, propStats, actualDocumentsArray, docNameProp, ownResult ) ;
		initPropStats(properties, propStats);
		gatherStats( properties, actualDocumentsArray, propStats, docNameProp ) ;
		processProps( properties, propStats ) ;
		ownResult.setLooksApplyable(true) ;
	}

	private IProperty getDocumentNameProperty(Iterable<IProperty> _properties) {
		IProperty docNameProp = null ;
		for( IProperty prop : _properties )
		{
			String propName = prop.getMeta().getSingleValue( "id", String.class, null) ;
			if( propName.equals( IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID ) )
				docNameProp = prop ;		
		}
		return docNameProp ;
	}

	private ArrayList<IProperty> extractNonserviceProps( Iterable<IProperty> _properties)
	{		
		ArrayList<IProperty> result = new ArrayList<IProperty>() ;
		for( IProperty prop : _properties )
		{
			Boolean isService = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_SERVICE_PROPERTY, Boolean.class, null ) ;
			if( isService != null && isService )
				continue ;
			
			result.add(prop) ;
		}		
		return result ;
	}

	public void initPropStats(Iterable<IProperty> properties, HashMap<IProperty, Integer> propStats)
	{
		propStats.clear() ;
		for( IProperty prop : properties ){
			String propName = prop.getMeta().getSingleValue( "id", String.class, null) ;
			propStats.put( prop, 0) ;
		}
	}	

	@SuppressWarnings("unused")
	private void gatherStats(Iterable<IProperty> properties, Iterable<Object> objects, HashMap<IProperty, Integer> propStats, IProperty docNameProp )
	{
		for( IProperty prop : properties )
		{			
			Boolean isService = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_SERVICE_PROPERTY, Boolean.class, null) ;
			if( isService != null && isService )
				continue ;
			
			Boolean isDepricated = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_DEPRICATED_LABEL, Boolean.class, null);
			if( isDepricated != null && isDepricated )
				continue ;
			
			String propName = prop.getMeta().getSingleValue( "id", String.class, null) ;
			System.out.print("") ;
			
			for( Object obj : objects )
			{
				//get docName
//				Object val = docNameProp.getValue(obj) ;
//				while( val instanceof IHasValue )
//					val = ((IHasValue)val).value() ;
			
//				String docName = (String) val ;
//				System.out.print("") ;			
				
				Object _val = prop.getValue(obj) ;
				if( _val != null )
				{
					int i1 = propStats.get(prop) ;
					propStats.put( prop, i1+1 ) ;
					
//					int i2 = docStats.get( docName ) ;
//					docStats.put( docName, i2+1 ) ;
				}				
			}
		}		
	}
	
	private void processDocs(Iterable<IProperty> properties,
			Iterable<Object> objects, final HashMap<IProperty, 
			Integer> propStats, ArrayList<Object> actualDocumentsArray
			,IProperty docNameProp, OwnAuditResult ownResult )
	{
		// TODO Auto-generated method stub
		
		ArrayList<IProperty> propList = new ArrayList<IProperty>() ;
		propList.addAll( propStats.keySet() ) ;
		
		Collections.sort( propList, new Comparator<IProperty>() {
			@Override
			public int compare( IProperty o1, IProperty o2) {
				int i = propStats.get(o2) - propStats.get(o1);				
				return i;
			}
		} ) ;


		ArrayList<IProperty> commonPropertyList = new ArrayList<IProperty>( actualPropertiesCount ) ;
		int count = 0 ;
		for( int i = 0 ; count < actualPropertiesCount && i < propList.size(); i++ ){
			IProperty prop = propList.get(i);
			
			if( prop.getId().equals( IPropertyConstants.TEXT_CONTENT_PROPERTY_NAME ) )
				continue ;
			
			if( prop.getId().equals( IPropertyConstants.DOCUMENT_NAME_PROPERTY_METADATA_ID ) )
				continue ;
			
			if( prop.getId().equals( "name" ) )
				continue ;
			
			commonPropertyList.add( prop ) ;
			count++ ;			
		}
		
		System.out.print(actualPropertiesCount+" common properties:\n") ;
		for( IProperty prop : commonPropertyList )
		{
			String pName = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_PROPERTY_NAME_LABEL,String.class, null ) ;
			System.out.print(pName) ;
			System.out.print("\n") ;
		}
		System.out.print("\n") ;
		
		IProperty actualityProp = null ;	
		for( IProperty prop : properties )
		{
			String propName = prop.getMeta().getSingleValue( "id", String.class, null) ;
			if( propName.equals( IPropertyConstants.ACTUALITY_PROPERTY_METADATA_ID ) )
				actualityProp = prop ;
		}
		
		HashSet<String> actualDocumentsSet = ownResult.getActualDocumentsSet() ;
l0:		for( Object obj : objects )
		{
			if(!(obj instanceof ICanBeActual) )
				continue ;
			
			for( IProperty prop : commonPropertyList )
			{
				String pName = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_PROPERTY_NAME_LABEL,String.class, null ) ;
				
				if( prop.getValue(obj) != null )
				{
					String docNmae = ((IThing)obj).getId() ;
					actualDocumentsSet.add(docNmae) ;
					actualDocumentsArray.add(obj) ;
					continue l0 ;					
				}
			}			
		}
		
		
		
//		System.out.print( "\n\nNot actual documents :\n\n" ) ;
//		for( Object obj : objects )
//		{
//			if(!(obj instanceof ICanBeActual) )
//				continue ;
//			
//			ICanBeActual obj_ = (ICanBeActual) obj ;
//			if( actualityProp.getValue(obj_) != null )
//			{	
//				Object val = docNameProp.getValue( obj_ ) ;
//				while( val instanceof IHasValue )
//					val = ((IHasValue)val).value() ;
//				
//				String docName = (String)val ; 
//				System.out.print( docName + '\n' ) ;
//			}			
//		}
//		
//		System.out.print( "\n\nActual documents :\n\n" ) ;
//		for( Object obj : objects )
//		{
//			if(!(obj instanceof ICanBeActual) )
//				continue ;
//			
//			ICanBeActual obj_ = (ICanBeActual) obj ;
//			if( actualityProp.getValue(obj_) == null )
//			{	
//				Object val = docNameProp.getValue( obj_ ) ;
//				while( val instanceof IHasValue )
//					val = ((IHasValue)val).value() ;
//				
//				String docName = (String)val ; 
//				System.out.print( docName + '\n' ) ;
//			}			
//		}
		
		
//		ArrayList<String> docList = new ArrayList<String>() ;
//		docList.addAll( docStats.keySet() ) ;
//		
//		Collections.sort(  docList, new Comparator<String>() {
//			@Override
//			public int compare(String o1, String o2) {
//				return  docStats.get(o1) -  docStats.get(o2);
//			}
//		} ) ;
//		
//		System.out.print( "\n\n"+"Documents\n\n" ) ;
//		for( String docName : docList )
//			System.out.print( docName + ": " + docStats.get( docName ) + '\n' ) ;
//		
//		return null ;
	}

	private static int criticalCount = 25 ;
	
	private void processProps(Iterable<IProperty> properties, final HashMap<IProperty, Integer> propStats)
	{
		ArrayList<IProperty> propList = new ArrayList<IProperty>() ;
		propList.addAll( propStats.keySet() ) ;
		
		Collections.sort( propList, new Comparator<IProperty>() {
			@Override
			public int compare( IProperty o1, IProperty o2) {
				int i = propStats.get(o2) - propStats.get(o1);				
				return i;
			}
		} ) ;


		System.out.print( "\nNon actual properties:\n" ) ;
		int ind = propList.size() ;
		for( ; ind > 0 && propStats.get( propList.get(ind-1)) < criticalCount ; ind-- )
		{
			IProperty prop = propList.get( ind-1 );
//			System.out.print( prop.getId() ) ;
//			System.out.print( " - " ) ;
//			System.out.print( propStats.get(prop) ) ;
//			System.out.print( "\n" ) ;
			IMeta meta = prop.getMeta().getParentMeta() ;
			IWritableMeta wMeta = (IWritableMeta) meta ;
			wMeta.putMeta( IPropertyConstants.CHAR_DEPRICATED_LABEL, true ) ;
		}
		
		System.out.print( "\nActual properties:\n" );
		for( int i = 0 ; i < ind ; i++ )
		{
			IProperty prop = propList.get( i );
			System.out.print( prop.getId() ) ;
			System.out.print( " - " ) ;
			System.out.print( propStats.get(prop) ) ;
			System.out.print( "\n" ) ;			
		}
		System.out.print( "\n\n" );
		System.out.print( "\nNon Actual properties:\n" );
		for( int i = ind ; i < propList.size() ; i++ )
		{
			IProperty prop = propList.get( i );
			System.out.print( prop.getId() ) ;
			System.out.print( " - " ) ;
			System.out.print( propStats.get(prop) ) ;
			System.out.print( "\n" ) ;
		}
		System.out.print( "\n\n" );
	}
	
	@Override
	public AbstractAuditResult createAuditResult() {
		return new OwnAuditResult( this );
	}

	@Override
	public String getId() {		
		return ID;
	}
	
	public static class OwnAuditResult extends AbstractAuditResult
	{
		private static final long serialVersionUID = -4131878306490045950L;
		
		public OwnAuditResult( IUniverseAudit audit ) {
			super(audit) ;
		}
		
		private final HashSet<String> actualDocuments = new HashSet<String>() ;
		
		public HashSet<String> getActualDocumentsSet() {
			return actualDocuments;
		}		
	}

	@Override
	public Set<String> getRequiredAudits() {
		// TODO Auto-generated method stub
		return null;
	}
	
	
}
